#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "Common.hpp"
#include "InitAddr.hpp"

const static int defaultsockfd = -1;

// 服务器是禁止拷贝的——具体方法：继承一个不能拷贝复制、赋值重载的基类
class TcpServer : public Nocopy
{
public:
    TcpServer(uint16_t port = defaultsockfd)
        : _port(port), _isrunning(false)
    {
        //signal(SIGCHLD, SIG_IGN); //忽略子进程退出信号，让父进程不要阻塞等待
        // 全双工、补包的tcp通信方式——SOCK_STREAM报文类型
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            std::cout << "create sockfd fail" << std::endl;
            exit(SOCKET_ERR);
        }
        std::cout << "sockfd success: " << _listensockfd << std::endl;

        InitAddr local(_port); // ip地址直接采取随机

        int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());
        if (n < 0)
        {
            std::cout << "bind fail" << std::endl;
            exit(BIND_ERR);
        }
        std::cout << "bind success" << std::endl;

        // 设置socket状态位listen
        // 只要tcp处于listen状态，那么他就可以被链接了
        n = listen(_listensockfd, 1024);
        if (n < 0)
        {
            std::cout << "listen fail" << std::endl;
            exit(LISTEN_ERR);
        }
        std::cout << "listen success" << std::endl;
    }

    class ThreadData//这个类用来存放各种子线程需要拥有的信息
    {
    public:
        int _sockfd;
        InitAddr _addr;  
        TcpServer* _tsvr;      
    };

    //长服务：
    //短服务：多线程多进程比较合适
    void Service(int sockfd, InitAddr &addr)
    {
        while (1)
        {
            // 先读取数据——读取tcp用read
            char buffer[1024];
            ssize_t m = read(sockfd, buffer, sizeof(buffer) - 1);
            // 通过这种方式读上来的可能不是一整个单词；而udp不存在这样的问题
	    // 因为udp要读就把信息全都读完.
	    // 文件本质就是面向字节流，哪部分怎么读就是协议决定的
	    // 这里需要根据字节流提取出完整的报文.
	    if (m < 0)
            {
                std::cout << "read fail" << std::endl;
                exit(1);
            }
            else if (m == 0)
            {
                // n==0说明对端把文件关闭了，读到了文件结束符的末尾
                std::cout << "Client exit" << std::endl;
                close(sockfd);
                break;
            }
            else
            {
                buffer[m] = 0;
                std::cout << addr.StringAddr() << " says@: " << buffer << std::endl;
                // 在写回数据
                write(sockfd, buffer, sizeof(buffer));
            }
        }
    }

    static void* Routine(void* args)
    {
        pthread_detach(pthread_self()); //用来解决父线程阻塞等待回收子线程的问题，让子线程运行完了自己自动退出
        ThreadData* td = (ThreadData*)args;
        td->_tsvr->Service(td->_sockfd, td->_addr);
        delete td;
        return nullptr;
    }

    void Run()
    {
        _isrunning = true;
        while (1)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(sockaddr_in);
            // 没有链接就会阻塞
            // 获取链接——后两个参数和recvfrom的后两个意义相同。链接从内核中直接获取，与函数无关
            int sockfd = accept(_listensockfd, CONV(peer), &len);
            if (sockfd < 0)
            {
                std::cout << "accept fail" << std::endl;
                exit(ACCEPT_ERR);
            }

            InitAddr addr(peer);
            std::cout << "accept success, peer addr: " << addr.StringAddr() << std::endl;

            // 多进程版本
            // int pid = fork();
            // if(pid < 0)
            // {
            //     std::cout << "fork fail" << std::endl;
            //     exit(-1);
            // }
            // if (pid == 0)
            // {
            //     //不想让后面调用服务的进程看到_listensockfd
            //     close(_listensockfd);
            //     if(fork() > 0)//立马就会被父进程回收，孙子进程去调用服务，最后孙子进程成为孤儿进程，最后被系统回收
            //         exit(OK);
            //     Service(sockfd, addr);
            //     exit(OK);
            // }
            // else
            // {
            //     //这里不能关闭用于连接的_listensockfd，否则客户端将无法连接
            //     close(sockfd);
            //     waitpid(pid, nullptr, 0);
            // }

            //多线程版本
            ThreadData* td = new ThreadData();
            td->_addr = addr;
            td->_sockfd = sockfd;
            td->_tsvr = this;
            pthread_t tid;
            pthread_create(&tid, nullptr, Routine, (void*)td);
        }
        _isrunning = false;
    }

    void Init() {}

    void Start() {}

    ~TcpServer() {}

private:
    uint16_t _port;
    bool _isrunning;
    int _listensockfd;
};
